#include <string.h>
#include <yonex/types.h>
#include <stddef.h>
#include <stdint.h>
#include <yonex/yonex.h>

void *memset(void *dest, int c, size_t n)
{
    unsigned char *s = dest;
    size_t k;

#ifdef __GNUC__
/*
    头尾处尽量使用较少的分支
    每个条件确保后续使用的偏移量都是明确定义的
    并且位于目标区域内
*/
    if (!n) return dest;
    s[0] = c;
    s[n-1] = c;
    if (n <= 2) return dest;
    s[1] = c;
    s[2] = c;
    s[n-2] = c;
    s[n-3] = c;
    if (n <= 6) return dest;
    s[3] = c;
    s[n-4] = c;
    if (n <= 8) return dest;

/*
    将指针向前移动，使其对齐到一个4字节边界
    并截断n使其能被4整除
    之前的代码已经考虑到任何被截断的头部或尾部。
*/
    k = -(uintptr_t)s & 3;
    s += k;
    n -= k;
    n &= -4;

    u32 c32 = ((u32)0x01010101) * (unsigned char)c;

    *(u32 *)(s+0) = c32;
	*(u32 *)(s+n-4) = c32;
	if (n <= 8) return dest;
	*(u32 *)(s+4) = c32;
	*(u32 *)(s+8) = c32;
	*(u32 *)(s+n-12) = c32;
	*(u32 *)(s+n-8) = c32;
	if (n <= 24) return dest;
	*(u32 *)(s+12) = c32;
	*(u32 *)(s+16) = c32;
	*(u32 *)(s+20) = c32;
	*(u32 *)(s+24) = c32;
	*(u32 *)(s+n-28) = c32;
	*(u32 *)(s+n-24) = c32;
	*(u32 *)(s+n-20) = c32;
	*(u32 *)(s+n-16) = c32;

/*
    对齐到8的倍数
    这样我们就可以一次填充64位
    并且在不引入额外分支的情况下
    尽可能避免写入相同的字节两次
*/
    k = 24 + ((uintptr_t)s & 4);
	s += k;
	n -= k;

    u64 c64 = c32 | ((u64)c32 << 32);
	for (; n >= 32; n-=32, s+=32) {
		*(u64 *)(s+0) = c64;
		*(u64 *)(s+8) = c64;
		*(u64 *)(s+16) = c64;
		*(u64 *)(s+24) = c64;
	}

    return dest;

#else
    for(; n; n--) *s++ = c;
    return dest;
#endif
}
